package com.hxzy.springbootes.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hxzy.springbootes.es.entity.EsHotel;
import com.hxzy.springbootes.es.service.EsHotalService;
import com.hxzy.springbootes.mybatis.entity.TbHotel;
import com.hxzy.springbootes.mybatis.service.TbHotelService;
import com.hxzy.springbootes.req.HotelReq;
import com.hxzy.springbootes.resp.HotelResp;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author wy
 * @Description TODO
 * @Date 2023/5/31 15:55
 * @Version 1.0
 */
@RestController
public class EsHotalSearchController {


    /**
     * 必须要去配置EsConfig
     */
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private ObjectMapper  objectMapper;

    /**
     * GET /hotel/_search
     * {
     *   "query": {
     *     "match_all": {}
     *   }
     * }
     * @param page
     * @param size
     * @return
     */
    @GetMapping(value = "/es/all")
    public Map<String,Object> findAll(int page,int size) throws IOException {
        // 1.准备Request  GET /hotel/_search
        SearchRequest request = new SearchRequest("hotel");
        //2、构造一个 match_all查询对象
        MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
        //3、设定查询条件
        SearchSourceBuilder query = request.source().query(matchAllQueryBuilder);
        //4、设定分页 第1页，要设定为0、
        int offset = (page - 1) * size;
        query.from(offset);
        query.size(size);

        return searchResult(request);
    }

    /**
     * 查询并返回结果
     * @param request
     * @return
     * @throws IOException
     */
    private Map<String,Object> searchResult(SearchRequest request) throws IOException {
        //5、以下都是固定的代码，可以封装，  发送dsl查询，返回查询结果对象
        SearchResponse searchResponse = this.restHighLevelClient.search(request, RequestOptions.DEFAULT);
        SearchHits hits = searchResponse.getHits();

        //总记录数
        long total=hits.getTotalHits().value;
        //当前页的记录数
        SearchHit[] searchHits = hits.getHits();
        List<EsHotel>  arrList=new ArrayList<>();

        for(SearchHit  hit: searchHits){
            //拿到文档ID
            int docId = hit.docId();
            System.out.println(docId);
            //直接拿到json数据
            String jsonStr=hit.getSourceAsString();
            EsHotel esHotel = this.objectMapper.readValue(jsonStr, EsHotel.class);
            arrList.add(esHotel);
        }
        //输出结果
        Map<String,Object> mp=new HashMap<>();
        mp.put("total", total);
        mp.put("rows",arrList);
        return mp;
    }


    /**
     * 根据名称查询
     * @param page
     * @param keyword
     * @return
     * @throws IOException
     */
    @GetMapping(value = "/es/search/byName")
    public Map<String, Object> searchByName(int page, String keyword) throws IOException {

        // 1.准备Request
        SearchRequest request = new SearchRequest("hotel");
        // 2.准备DSL
        //分页
        int offset= (page-1)*10;
        SearchSourceBuilder all = request.source().query(QueryBuilders.matchQuery("all", keyword)).from(offset).size(10);

        return searchResult(request);
    }


    /**  GET http://localhost:8080/es/search?page=1&size=10&key=酒店&price=200-500&sorted=2&city=深圳
     * 复杂条件查询
     * @param req
     * @return
     */
    @GetMapping(value = "/es/search")
    public Map<String,Object> searchByCondition(HotelReq req) throws IOException {

        // 1.准备Request
        SearchRequest request = new SearchRequest("hotel");

        //2、bool条件组装
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        //得到一个 and 条件集合
        List<QueryBuilder> mustList = boolQueryBuilder.must();
        if(req.getKey()!=null && req.getKey()!=""){
            //match检索
            mustList.add(QueryBuilders.matchQuery("all",req.getKey()));
        }
        //城市不为空
        if(req.getCity()!=null && req.getCity()!=""){
            //精确匹配  term
            mustList.add(QueryBuilders.termQuery("city", req.getCity()));
        }
        //品牌不为空
        if(req.getBrand()!=null && req.getBrand()!=""){
            //精确匹配  term
            mustList.add(QueryBuilders.termQuery("brand", req.getBrand()));
        }
        //选择了几星
        if(req.getStarname()!=null && req.getStarname()!=""){
            //精确匹配  term
            mustList.add(QueryBuilders.termQuery("starname", req.getStarname()));
        }

        //选择了价格    价格0-150, 100-300 ,   1500
        if(req.getPrice()!=null && req.getPrice()!="") {
            int min=0;
            int max=0;
            RangeQueryBuilder priceBuilder=null;
            String[] priceArr = req.getPrice().split("-");
            if(priceArr.length==1){
               max=Integer.parseInt(priceArr[0]);
               priceBuilder = QueryBuilders.rangeQuery("price").gte(max);

            }else{
                min=Integer.parseInt(priceArr[0]);
                max=Integer.parseInt(priceArr[1]);
                priceBuilder=QueryBuilders.rangeQuery("price").gte(min).lte(max);
            }
            boolQueryBuilder.filter(priceBuilder);
        }

        //一个条件都没有
        if(boolQueryBuilder.must().size()==0){
            throw new RuntimeException("抱歉，没有找到与关键词相关的商品");
        }else{
            request.source().query(boolQueryBuilder);
        }


        //排序 1价格升序， 2价格降序，3评分高，4评分低
        if(req.getSorted()!=null){
           if(req.getSorted()==1){
               request.source().sort("price", SortOrder.ASC);
           }else if(req.getSorted()==2){
               request.source().sort("price", SortOrder.DESC);
           }else if(req.getSorted()==3){
               request.source().sort("score", SortOrder.DESC);
           }else if(req.getSorted()==4){
               request.source().sort("socre", SortOrder.ASC);
           }
        }else{
            request.source().sort("price", SortOrder.ASC);
        }

        //排序
        request.source().from( (req.getPage()-1) * req.getSize());
        request.source().size(req.getSize());

        //查询
        return searchResult(request);
    }


    /**
     * 查询 name变高亮
     * @param req
     * @return
     * @throws IOException
     */
    @PostMapping(value = "/es/search/highlight")
    public HotelResp searchByConditionHighLight( @RequestBody HotelReq req) throws IOException {

        // 1.准备Request
        SearchRequest request = new SearchRequest("hotel");

        //2、bool条件组装
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        //得到一个 and 条件集合
        List<QueryBuilder> mustList = boolQueryBuilder.must();
        if(req.getKey()!=null && req.getKey()!=""){
            //match检索
            mustList.add(QueryBuilders.matchQuery("all",req.getKey()));
        }
        //城市不为空
        if(req.getCity()!=null && req.getCity()!=""){
            //精确匹配  term
            mustList.add(QueryBuilders.termQuery("city", req.getCity()));
        }
        //品牌不为空
        if(req.getBrand()!=null && req.getBrand()!=""){
            //精确匹配  term
            mustList.add(QueryBuilders.termQuery("brand", req.getBrand()));
        }
        //选择了几星
        if(req.getStarname()!=null && req.getStarname()!=""){
            //精确匹配  term
            mustList.add(QueryBuilders.termQuery("starname", req.getStarname()));
        }

        //选择了价格    价格0-150, 100-300 ,   1500
        if(req.getPrice()!=null && req.getPrice()!="") {
            int min=0;
            int max=0;
            RangeQueryBuilder priceBuilder=null;
            String[] priceArr = req.getPrice().split("-");
            if(priceArr.length==1){
                max=Integer.parseInt(priceArr[0]);
                priceBuilder = QueryBuilders.rangeQuery("price").gte(max);

            }else{
                min=Integer.parseInt(priceArr[0]);
                max=Integer.parseInt(priceArr[1]);
                priceBuilder=QueryBuilders.rangeQuery("price").gte(min).lte(max);
            }
            boolQueryBuilder.filter(priceBuilder);
        }

        //一个条件都没有
        if(boolQueryBuilder.must().size()==0){
            //throw new RuntimeException("抱歉，没有找到与关键词相关的商品");
            //查询所有的条件
            request.source().query(QueryBuilders.matchAllQuery());

        }else{
            request.source().query(boolQueryBuilder);
        }


        //排序 1价格升序， 2价格降序，3评分高，4评分低
        if(req.getSorted()!=null){
            if(req.getSorted()==1){
                request.source().sort("price", SortOrder.ASC);
            }else if(req.getSorted()==2){
                request.source().sort("price", SortOrder.DESC);
            }else if(req.getSorted()==3){
                request.source().sort("score", SortOrder.DESC);
            }else if(req.getSorted()==4){
                request.source().sort("price", SortOrder.ASC);
            }
        }else{
            request.source().sort("starName", SortOrder.ASC);
        }

        //排序
        request.source().from( (req.getPage()-1) * req.getSize());
        request.source().size(req.getSize());

        //设定高亮的条件和前缀和后缀 html标签
//        "highlight": {
//            "fields":{
//                "name": {
//                    "require_field_match": "false",
//                            "pre_tags":"<font class='skcolor_ljg'>",
//                            "post_tags":"</font>"
//                }
//            }
//        }

        request.source().highlighter(new HighlightBuilder().field("name").preTags("<font class=\"skcolor_ljg\">").postTags("</font>").requireFieldMatch(false));


        //查询
        HotelResp  resp=hightLighterResult(request);

        return resp;
    }

    private HotelResp  hightLighterResult(SearchRequest request) throws IOException {
        // 3.发送请求
        SearchResponse response = this.restHighLevelClient.search(request,RequestOptions.DEFAULT);

        //4、得到结果
        SearchHits hits = response.getHits();

        //5、得到总笔数
        long count=hits.getTotalHits().value;

        //6、得到明细
        List<EsHotel> arrList=new ArrayList<>();

        SearchHit[] hitsHits = hits.getHits();
        //7、循环再一次读取数据
        for (SearchHit  ht : hitsHits ) {
            // 获取文档source
            String json = ht.getSourceAsString();
            // 反序列化
            EsHotel esHotel = objectMapper.readValue(json, EsHotel.class);

            //还要解析高亮字段
//            "highlight" : {
//                "name" : [
//                     "<font class='skcolor_ljg'>如家</font>酒店(北京良乡西路店)"
//                  ]
//            }
            //把高亮字段的结果，替换内存中的原始的值
            Map<String, HighlightField> highlightFields = ht.getHighlightFields();
            if(highlightFields!=null && highlightFields.containsKey("name")){
                HighlightField nameField = highlightFields.get("name");
                Text[] fragments = nameField.getFragments();
                String nameValue=fragments[0].string();
                esHotel.setName(nameValue);
            }

            arrList.add(esHotel);
        }

        //装组对象
        HotelResp resp=new HotelResp();
        resp.setTotal(count);
        resp.setRows(arrList);
        return resp;
    }

}
